package com.ruoyi.ade.service.impl;

import com.alibaba.fastjson2.JSONArray;
import com.ruoyi.ade.domain.AdeLab;
import com.ruoyi.ade.domain.AdeLabKfoldResult;
import com.ruoyi.ade.domain.AdeLabResult;
import com.ruoyi.ade.domain.Standard;
import com.ruoyi.ade.mapper.AdeLabKfoldResultMapper;
import com.ruoyi.ade.mapper.AdeLabMapper;
import com.ruoyi.ade.mapper.AdeLabResultMapper;
import com.ruoyi.ade.mapper.StandardMapper;
import com.ruoyi.ade.service.IAdeAnalyzeService;
import com.ruoyi.ade.tool.AdeCompareUtils;
import com.ruoyi.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * AdeAnalyzeServiceImpl
 * <p>
 * ADE 分析服务实现类
 */
@Service
@Slf4j
public class AdeAnalyzeServiceImpl implements IAdeAnalyzeService {

    @Autowired
    private AdeLabMapper adeLabMapper;
    @Autowired
    private StandardMapper standardMapper;

    @Autowired
    private AdeLabKfoldResultMapper adeLabKfoldResultMapper;
    @Autowired
    private AdeLabResultMapper adeLabResultMapper;

    @Override
    public void execute(Long id) {

        AdeLab adeLab = adeLabMapper.selectAdeLabById(id);
        if (adeLab == null) throw new RuntimeException("No lab found for:" + id);

        if (!AdeLab.STATUS_READY.equals(adeLab.getStatus()) && !AdeLab.STATUS_FAILED.equals(adeLab.getStatus()))
            throw new RuntimeException("Current lab status exception:" + adeLab.getStatus());

        if (adeLab.getFolds() == null || (adeLab.getFolds() < 1 || adeLab.getFolds() > 20))
            throw new RuntimeException("Current lab folds exception:" + adeLab.getFolds());

        if (adeLab.getSize() == null || (adeLab.getSize() < 100))
            throw new RuntimeException("Current lab size exception:" + adeLab.getSize());

        Long fold = adeLab.getFolds();
        //验证完毕 启动任务执行
        new Thread(new Runnable() {
            @Override
            public void run() {
                //更新实验状态为进行中
                AdeLab update = new AdeLab();
                update.setId(id);
                update.setStatus(AdeLab.STATUS_RUNNING);
                adeLabMapper.updateAdeLab(update);
                log.info("Lab id:{} is running...", id);

                try {
                    //获取可用的标准数据集
                    List<Standard> avis = getAviStandard(adeLab);
                    log.info("Lab id:{}, Get avi standards size:{}", id, avis.size());

                    //可用数量的变更   batchsize
                    long batch_size = avis.size() > adeLab.getSize() ? adeLab.getSize() : avis.size();
                    log.info("Lab id:{}, Confirmed batch size:{}", id, batch_size);
                    if (batch_size == 0) throw new RuntimeException("Current lab size exception:" + batch_size);

                    List<AdeLabResult> list = new ArrayList<>();
                    //启动 fold 个批次的执行
                    for (int i = 0; i < fold; i++) {
                        log.info("Lab id:{}, Executing fold:{} ", id, (i + 1));
                        //获取到随机的标准数据集
                        List<Standard> standards = random(avis, batch_size);
                        log.info("Lab id:{}, Get random standards:{}", id, standards.size());
                        //分析 12个算法 * 3个级别的分析结果
                        List<AdeLabResult> results = analyze(id,(i+1), standards);
                        //结果批量入库
                        System.out.println(JSONArray.toJSONString(results));
                        adeLabResultMapper.insertAdeLabResultBatch(results);
                        list.addAll(results);
                        log.info("Lab id:{}, Fold index:{} finished, produced lab results:{}", id, (i + 1), results.size());
                    }

                    //最终结果 4个方法*3个提示*3个级别
                    List<AdeLabKfoldResult> kfoldResults = new ArrayList<>();
                    //计算各个算法的综合指标   统计每个算法每个级别的k fold分析结果
                    for (int i = 0; i < ALGORITHMS.length; i++) {
                        log.info("Lab id:{}, Analyzing algorithm:{} fold result.", id, ALGORITHMS[i]);
                        List<AdeLabResult> tmpsL1 = new ArrayList<>();
                        List<AdeLabResult> tmpsL2 = new ArrayList<>();
                        List<AdeLabResult> tmpsL3 = new ArrayList<>();

                        String algorithm = ALGORITHMS[i];
                        //每个算法 3个级别的结果
                        for (AdeLabResult result : list) {
                            if (!algorithm.equals(result.getAlgorithm())) continue;
                            if (AdeCompareUtils.LEVEL_L1.equals(result.getAdeMatchLevel())) {
                                tmpsL1.add(result);
                            }
                            if (AdeCompareUtils.LEVEL_L2.equals(result.getAdeMatchLevel())) {
                                tmpsL2.add(result);
                            }
                            if (AdeCompareUtils.LEVEL_L3.equals(result.getAdeMatchLevel())) {
                                tmpsL3.add(result);
                            }
                        }

                        kfoldResults.add(new AdeLabKfoldResult(id, algorithm, PROMPTTYPES[i], LLMS[i], AdeCompareUtils.LEVEL_L1, tmpsL1));
                        kfoldResults.add(new AdeLabKfoldResult(id, algorithm, PROMPTTYPES[i], LLMS[i], AdeCompareUtils.LEVEL_L2, tmpsL2));
                        kfoldResults.add(new AdeLabKfoldResult(id, algorithm, PROMPTTYPES[i], LLMS[i], AdeCompareUtils.LEVEL_L3, tmpsL3));
                        log.info("Lab id:{}, Finish algorithm:{} fold result.", id, ALGORITHMS[i]);
                    }

                    //最终k折分析结果入库
                    adeLabKfoldResultMapper.batchInsertAdeLabKfoldResults(kfoldResults);
                } catch (Exception e) {
                    e.printStackTrace();
                    update.setStatus(AdeLab.STATUS_FAILED);
                    adeLabMapper.updateAdeLab(update);
                    log.info("Lab id:{}, Finished update status:failed, reason:{}", id, e.getMessage());
                    return;  //一定要返回
                }
                //更新实验状态为成功
                update.setStatus(AdeLab.STATUS_SUCCESS);
                adeLabMapper.updateAdeLab(update);
                log.info("Lab id:{}, Finished update status:success", id);
            }
        }).start();
    }

    private static final String[] ALGORITHMS = new String[]{
            "nag_enrie",
            "nag_deepseek",
            "nag_gpt4o",
            "nag_qwen",
            "sag_enrie",
            "sag_deepseek",
            "sag_gpt4o",
            "sag_qwen",
            "rag_enrie",
            "rag_deepseek",
            "rag_gpt4o",
            "rag_qwen"
    };

    private static final String[] PROMPTTYPES = new String[]{
            "NAG",
            "NAG",
            "NAG",
            "NAG",
            "SAG",
            "SAG",
            "SAG",
            "SAG",
            "RAG",
            "RAG",
            "RAG",
            "RAG"
    };

    private static final String[] LLMS = new String[]{
            "ernie-3.5-8k",
            "deepseek-v3",
            "gpt-4o",
            "qwen-turbo",
            "ernie-3.5-8k",
            "deepseek-v3",
            "gpt-4o",
            "qwen-turbo",
            "ernie-3.5-8k",
            "deepseek-v3",
            "gpt-4o",
            "qwen-turbo"
    };

    private List<AdeLabResult> analyze(Long labId, int foldIndex, List<Standard> standards) {

        List<AdeLabResult> results = new ArrayList<>();

        //第一维度是算法  第二维度分别是 0-l1 1-l2 2-l3
        int tp[][] = new int[12][3];
        int tn[][] = new int[12][3];
        int fp[][] = new int[12][3];
        int fn[][] = new int[12][3];

        for (Standard standard : standards) {
            boolean referencePos = (!"[]".equals(standard.getAdes()));

            compute(referencePos, standard.getNagAdesEnireRes(), 0, tp, tn, fp, fn);
            compute(referencePos, standard.getNagAdesDeepseekRes(), 1, tp, tn, fp, fn);
            compute(referencePos, standard.getNagAdesGptRes(), 2, tp, tn, fp, fn);
            compute(referencePos, standard.getNagAdesQwenRes(), 3, tp, tn, fp, fn);

            compute(referencePos, standard.getAdesEnireRes(), 4, tp, tn, fp, fn);
            compute(referencePos, standard.getAdesDeepseekRes(), 5, tp, tn, fp, fn);
            compute(referencePos, standard.getAdesGptRes(), 6, tp, tn, fp, fn);
            compute(referencePos, standard.getAdesQwenRes(), 7, tp, tn, fp, fn);

            compute(referencePos, standard.getRagAdesEnireRes(), 8, tp, tn, fp, fn);
            compute(referencePos, standard.getRagAdesDeepseekRes(), 9, tp, tn, fp, fn);
            compute(referencePos, standard.getRagAdesGptRes(), 10, tp, tn, fp, fn);
            compute(referencePos, standard.getRagAdesQwenRes(), 11, tp, tn, fp, fn);
        }

        for (int i = 0; i < ALGORITHMS.length; i++) {  //每个算法 有三个级别对应的分析结果
            results.add(new AdeLabResult(labId, foldIndex, ALGORITHMS[i], PROMPTTYPES[i], LLMS[i], AdeCompareUtils.LEVEL_L1, tp[i][0], tn[i][0], fp[i][0], fn[i][0]));
            results.add(new AdeLabResult(labId, foldIndex, ALGORITHMS[i], PROMPTTYPES[i], LLMS[i], AdeCompareUtils.LEVEL_L2, tp[i][1], tn[i][1], fp[i][1], fn[i][1]));
            results.add(new AdeLabResult(labId, foldIndex, ALGORITHMS[i], PROMPTTYPES[i], LLMS[i], AdeCompareUtils.LEVEL_L3, tp[i][2], tn[i][2], fp[i][2], fn[i][2]));
        }

        return results;
    }

    /**
     * @param level 比对结果值
     * @param index 算法索引
     * @param tp
     * @param tn
     * @param fp
     * @param fn
     */
    private void compute(boolean referencePos, String level, int index, int[][] tp, int[][] tn, int[][] fp, int[][] fn) {
        switch (level) {
            case AdeCompareUtils.LEVEL_L1:     //识别准确   如果比对结果是L1级别， 那l2 ,l3级别也都是正确的
                if (referencePos) {  //如果参考集是  正    识别准确就是 true positive
                    tp[index][0]++;   //l1累计
                    tp[index][1]++;   //l2累计
                    tp[index][2]++;   //l3累计
                } else {              //如果参考集是  负      识别准确就是true negative
                    tn[index][0]++;
                    tn[index][1]++;
                    tn[index][2]++;
                }
                break;
            case AdeCompareUtils.LEVEL_L2:    //识别准确
                if (referencePos) { //如果参考集是  正    识别准确就是 true positive
                    tp[index][1]++;
                    tp[index][2]++;

                    fn[index][0]++;  //针对l1级别要求 则认为是识别错误了
                } else {             //如果参考集是  负      识别准确就是true negative
                    tn[index][1]++;
                    tn[index][2]++;

                    fp[index][0]++;  //针对L1级别要求 则认为是识别错误了
                }
                break;
            case AdeCompareUtils.LEVEL_L3:   //识别准确
                if (referencePos) {  //如果参考集是  正    识别准确就是 true positive
                    tp[index][2]++;

                    fn[index][0]++;
                    fn[index][1]++;
                } else {           //如果参考集是  负      识别准确就是true negative
                    tn[index][2]++;

                    fp[index][0]++;
                    fp[index][1]++;
                }
                break;
            case AdeCompareUtils.LEVEL_LN:    //识别错误
                if (referencePos) {
                    fn[index][0]++;
                    fn[index][1]++;
                    fn[index][2]++;
                } else {
                    fp[index][0]++;
                    fp[index][1]++;
                    fp[index][2]++;
                }

                break;
        }
    }

    /**
     * @param adeLab
     * @return
     */
    private List<Standard> getAviStandard(AdeLab adeLab) {
        List<Standard> standards = standardMapper.selectStandardList(new Standard());
        List<Standard> avis = new ArrayList<>();
        List<String> includes = new ArrayList<>();
        List<String> excludes = new ArrayList<>();

        if (adeLab.getIncludeSource() != null) {
            String[] include = adeLab.getIncludeSource().split("#");
            for (String s : include) {
                if (!StringUtils.isEmpty(s))
                    includes.add(s);
            }
        }
        log.info("Lab Name:{}, Include sources:{}", adeLab.getName(), JSONArray.toJSONString(includes));

        if (adeLab.getExcludeSource() != null) {
            String[] exclude = adeLab.getExcludeSource().split("#");
            for (String s : exclude) {
                if (!StringUtils.isEmpty(s))
                    excludes.add(s);
            }
        }
        log.info("Lab Name:{}, Exclude sources:{}", adeLab.getName(), JSONArray.toJSONString(excludes));

        String standardStatus = adeLab.getStandardStatus();

        //分析符合要求的 标准
        for (Standard standard : standards) {
//            System.out.println(standardStatus+"==============:"+standard.getSource()+"\t\t"+!excludes.contains(standard.getSource()));
            if (StringUtils.isEmpty(standard.getSource())) continue; //如果来源是空 则不考虑
            //纳入标准    必须是符合状态
            if (StringUtils.isEmpty(standardStatus) || standard.getStatus().equals(standardStatus)) {
                if (includes.size() == 0 || includes.contains(standard.getSource())) {
                    //排除标准
                    if (excludes.size() == 0 || !excludes.contains(standard.getSource())) {
                        avis.add(standard);
                    }
                }
            }
        }
        return avis;
    }

    /**
     * 随机获取标准数据集
     *
     * @param avis
     * @param batch_size
     * @return
     */
    private List<Standard> random(List<Standard> avis, long batch_size) {
        List<Standard> result = new ArrayList<>();

        //如果需要分析的数量 大于可用的数量 则直接返回可用的数量
        if (batch_size >= avis.size()) return avis;

        //随机获取
        byte[] flag = new byte[avis.size()];
        for (int i = 0; i < batch_size; i++) {
            int index = (int) (Math.random() * avis.size());
            while (flag[index] == 1) {
                index = (int) (Math.random() * avis.size());
            }
            flag[index] = 1;
            result.add(avis.get(index));
        }
        return result;
    }
}
